package org.hit.burkun.swalk.lap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

import org.apache.commons.math3.util.Pair;
import org.hit.burkun.entities.Entity;
import org.hit.burkun.network.EdgeInfo;
import org.hit.burkun.network.GraphFeatures;
import org.hit.burkun.network.SerializableGraph;
import org.hit.burkun.network.Vertex;
import org.hit.burkun.network.tester.ValidData;
import org.hit.burkun.swalk.FeatureExtracter;
import org.hit.burkun.swalk.FeatureField;
import org.hit.burkun.swalk.RandomWalkGraph;
import org.hit.burkun.swalk.Ranker;
import org.hit.burkun.swalk.SimplePageRank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cern.colt.matrix.tdouble.impl.DenseDoubleMatrix1D;

public class SeribaleGraphAdpater {
	private static Logger log = LoggerFactory
			.getLogger(SerializableGraph.class);

	// ************
	public static LinkedList<String> getRandomWalkWithRestartTrainValue(
			double[] param, HashMap<String, HashSet<EdgeInfo>> testTrue,
			HashMap<String, HashSet<EdgeInfo>> testFalse, SerializableGraph sg,
			double eps, double salpha, boolean useFeature) {

		// train
		LinkedList<String> res = new LinkedList<>();
		Set<String> trueKey = testTrue.keySet();
		for (String key : trueKey) {
			Vertex v = sg.getVertexByUmls(key);
			ArrayList<Integer> trueSet = getTestVertexByUmls(v.getUmlsId(),
					testTrue);
			HashSet<Integer> trueS = new HashSet<>(trueSet);
			ArrayList<Integer> falseSet = getTestVertexByUmls(v.getUmlsId(),
					testFalse);
			HashSet<Integer> falseS = new HashSet<>(falseSet);
			// 使用feature 和 param 来控制，当都为1时，就是无权重的
			RandomWalkGraph rg = getRandomWalkGraph(sg, v.getUid(), useFeature);
			ArrayList<Pair<Integer, Double>> highestRank = Ranker
					.rankAndSortRandWalkWithRestart(rg,
							new DenseDoubleMatrix1D(param), salpha, eps);
			for (Pair<Integer, Double> p : highestRank) {
				if (trueS.contains(p.getFirst())) {
					Vertex e = sg.getVertexByUid(p.getFirst());
					res.add(key + "-" + e + "$" + p.getSecond() + "-1");
				}

				if (falseS.contains(p.getFirst())) {
					Vertex e = sg.getVertexByUid(p.getFirst());
					res.add(key + "-" + e + "$" + p.getSecond() + "-0");
				}
			}
		}
		return res;
	}

	public static LinkedList<String> getBioGraphRankingByUmls(String umls,
			SerializableGraph sg, double eps, double galpha, double salpha,
			double[] parameters, boolean useFeature) {
		log.debug("get all pr from umls:" + umls);
		//获取验证数据
		HashSet<EdgeInfo> vaildData = ValidData.splitVaildData(sg, umls);
		LinkedList<String> res = new LinkedList<>();
		//得到全局的aplha为1的pr图
		SimplePageRank globalRg = getGlobalRandomWalkGraph(sg, useFeature);
		if (!useFeature) {
			parameters = new double[] { 1.0 };
		}
		// global pr
		int s = sg.getVertexByUmls(umls).getUid();
		ArrayList<Double> globalRank = Ranker.getGlobalPr(globalRg,
				new DenseDoubleMatrix1D(parameters), galpha, eps);
		for (EdgeInfo v : vaildData) {
			log.debug("get all pr from umls:" + umls + "-" + v);
			GraphFeatures.removeEdgeFromNetWork(sg, umls, v);
			//获取单个pr
			RandomWalkGraph rg = getRandomWalkGraph(sg, s, useFeature);
			ArrayList<Pair<Integer, Double>> highestRank = Ranker
					.rankAndSortRandWalkWithRestart(rg,
							new DenseDoubleMatrix1D(parameters), salpha, eps);
			ArrayList<Pair<Integer, Double>> normalHighestRank = new ArrayList<>();
			// save results
			for (int i = 0; i < highestRank.size(); i++) {
				Pair<Integer, Double> pair = highestRank.get(i);
				Vertex ev = sg.getVertexByUid(pair.getFirst());
				if (ev != null
						&& ev.isInNodeTypes(Entity.TYPE.GENE_ID.getTag())) {
					//除以总的pr，获得相对pr
					Pair<Integer, Double> p = new Pair<Integer, Double>(
							pair.getFirst(), pair.getSecond()
									/ globalRank.get(pair.getFirst()));
					normalHighestRank.add(p);
				}
			}
			//进行排序
			Collections.sort(normalHighestRank,
					new Comparator<Pair<Integer, Double>>() {
						@Override
						public int compare(Pair<Integer, Double> o1,
								Pair<Integer, Double> o2) {
							if (o1.getValue() > o2.getValue())
								return 1;
							if (o2.getValue() > o1.getValue())
								return -1;
							return 0;
						}
					});
			Collections.reverse(normalHighestRank);
			for (int i = 0; i < highestRank.size(); i++) {
				Pair<Integer, Double> pair = normalHighestRank.get(i);
				if (pair.getFirst() == v.getVertex().getUid()) {
					res.add(umls + ":" + v.getVertex() + ":"
							+ ((i + 1.0) / normalHighestRank.size()));
					break;
				}
			}
			// reverse
			GraphFeatures.addEdgeToNetWork(sg, umls, v, true);
			log.debug("get all pr from umls:" + umls + "-" + v + " done");
		}
		return res;
	}

	public static SimplePageRank getGlobalRandomWalkGraph(
			SerializableGraph sg, boolean useFeature) {
		int vNum = sg.getAllVertexs().size();
		ArrayList<FeatureField> list = null;
		// init feature, 使用特征么
		list = getFeatureList(sg, useFeature);
		//use feature = false时，不适用力度函数
		SimplePageRank net = new SimplePageRank(vNum,
				FeatureExtracter.getFeatureNum(), list, useFeature);
		return net;
	}

	public static RandomWalkGraph getRandomWalkGraph(SerializableGraph sg,
			int s, boolean useFeature) {
		int vNum = sg.getAllVertexs().size();
		// init feature
		ArrayList<FeatureField> list = getFeatureList(sg, useFeature);
		LinkedListNetworkLapc net = new LinkedListNetworkLapc(vNum, s,
				FeatureExtracter.getFeatureNum(), list, null, null,useFeature);
		return net;
	}

	// **************
	// train
	// **************

	public static HashMap<String, RandomWalkGraph> getRandomWalkTrainGraphs(HashMap<String, HashSet<EdgeInfo>> testTrueTrain,
			HashMap<String, HashSet<EdgeInfo>> testFalseTrain, HashMap<String, HashSet<EdgeInfo>> testTrue,
			SerializableGraph sg) {
		// size
		int vNum = sg.getAllVertexs().size();
		// init feature
		ArrayList<FeatureField> list = getFeatureList(sg, true);
		// build network
		Set<String> trueKey = testTrue.keySet();
		HashMap<String, RandomWalkGraph> res = new HashMap<>();
		for (String key : trueKey) {
			Vertex v = sg.getVertexByUmls(key);
			ArrayList<Integer> trueSet = getTestVertexByUmls(v.getUmlsId(),
					testTrueTrain);
			ArrayList<Integer> falseSet = getTestVertexByUmls(v.getUmlsId(),
					testFalseTrain);
			LinkedListNetworkLapc net = new LinkedListNetworkLapc(vNum, v.getUid(),
					FeatureExtracter.getFeatureNum(), list, trueSet, falseSet, true);
			res.put(v.getUmlsId(), net);
		}
		return res;
	}

	public static ArrayList<FeatureField> getFeatureList(SerializableGraph sg,
			boolean useFeature) {
		Collection<Vertex> vs = sg.getAllVertexs();
		ArrayList<FeatureField> list = new ArrayList<>();
		// maker! not add the bi-dir feature
		HashSet<String> maker = new HashSet<>();
		for (Vertex v : vs) {
			int v1idx = v.getUid();
			LinkedList<EdgeInfo> edges = sg.getNeighborsByVertex(v);
			for (EdgeInfo edge : edges) {
				int v2idx = edge.getVertex().getUid();
				if (!maker.contains(v1idx + "-" + v2idx)) {
					double[] features = null;
					if (useFeature) {
//						features = FeatureExtracter.extractFeaturesVersion8NoScore(v,edge.getVertex(), sg);
						features = FeatureExtracter.extractFeaturesVersion8Score(v,edge.getVertex(), sg);
//						features = FeatureExtracter.extractFeaturesVersion9PlusCn(v,edge.getVertex(), sg);
//						features = FeatureExtracter.extractFeaturesVersion10f(v,edge.getVertex(), sg);
//						features = FeatureExtracter.extractFeaturesVersion10WithScoref(v, edge.getVertex(), sg);

					} else {
						features = FeatureExtracter.extractFeaturesAllOne();
					}
					FeatureField ff = new FeatureField(v1idx, v2idx, features);
					list.add(ff); 
					maker.add(v1idx + "-" + v2idx);
					maker.add(v2idx + "-" + v1idx);
				}
			}
		}
		return normalized(list);
	}
	//归一化权值
	private static ArrayList<FeatureField> normalized( ArrayList<FeatureField> arr){
		int size = arr.size();
		int fn = FeatureExtracter.getFeatureNum();
		double[] nmax = new double[fn];
		double[] nmin = new double[fn];
		for(int i=0; i<nmin.length; i++){
			nmin[i] = Double.MAX_VALUE;
			nmax[i] = Double.MIN_VALUE;
		}
		
		for(int i=0; i< size; i++){
			for(int j=0; j<fn; j++){
				double ff  = arr.get(i).features.get(j);
				if(ff < nmin[j] ){
					nmin[j] = ff;
				}
				if(ff > nmax[j]){
					nmax[j] = ff;
				}
			}
		}
		for(int i=0; i< size; i++){
			for(int j=0; j<fn; j++){
				double v = arr.get(i).features.get(j) - nmin[j];
				double fenmu = nmax[j] - nmin[j];
				if(fenmu == 0){
					arr.get(i).features.set(j, 1);
				}else{
					arr.get(i).features.set(j, v/fenmu);
				}
			}
		}
		return arr;
	}
	
	// get false and true set
	private static ArrayList<Integer> getTestVertexByUmls(String umls,
			HashMap<String, HashSet<EdgeInfo>> trainSet) {
		HashSet<EdgeInfo> es = trainSet.get(umls);
		if(es == null){
			return new ArrayList<Integer>();
		}
		ArrayList<Integer> sets = new ArrayList<>();
		for (EdgeInfo e : es) {
			sets.add(e.getVertex().getUid());
		}
		return sets;
	}

}
